" --------------------------
" set asyncrun_open
" --------------------------
augroup SetRunOpen
    au VimResized,VimEnter * call s:SetRunRows()
augroup END
function! s:SetRunRows()
    let row = float2nr(&lines * 0.2)
    if row < 10 || get(g:, 'asyncrun_open', 10) < 10
        let g:asyncrun_open = 10
    else
        let g:asyncrun_open = row
    endif
endfunction
" --------------------------
" asyncrun
" --------------------------
if has('nvim') || has('timers') && has('channel') && has('job')
    let g:asyncrun_rootmarks = g:root_patterns
    let s:run_command = "AsyncRun"
    if WINDOWS()
        let g:asyncrun_encs = get(g:, 'asyncrun_encs', 'gbk')
    else
        let g:asyncrun_encs = get(g:, 'asyncrun_encs', 'utf-8')
    endif
    PlugAddOpt 'asyncrun.vim'
    if UNIX()
        silent! call mkdir(Expand("$HOME/.cache/build"), "p")
        if executable('gcc')
            let g:gcc_cmd = 'time gcc -Wall -O2 $(VIM_FILEPATH) -o ~/.cache/build/$(VIM_FILENOEXT) && echo && time ~/.cache/build/$(VIM_FILENOEXT)'
        endif
        if executable('g++')
            let g:gpp_cmd = 'time g++ -Wall -O2 $(VIM_FILEPATH) -o ~/.cache/build/$(VIM_FILENOEXT) && echo && time ~/.cache/build/$(VIM_FILENOEXT)'
        endif
        if executable('rustc')
            let g:rustc_cmd = 'time rustc -o ~/.cache/build/$(VIM_FILENOEXT) $(VIM_FILEPATH) && echo && time ~/.cache/build/$(VIM_FILENOEXT)'
        endif
    elseif WINDOWS()
        if executable('gcc')
            let g:gcc_cmd = 'ptime gcc $(VIM_FILEPATH) -o ..\target\test\$(VIM_FILENOEXT).exe & ptime ..\target\test\$(VIM_FILENOEXT).exe'
        endif
        if executable('g++')
            let g:gpp_cmd = 'ptime g++ $(VIM_FILEPATH) -o ..\target\test\$(VIM_FILENOEXT).exe & ptime ..\target\test\$(VIM_FILENOEXT).exe'
        endif
        if executable('rustc')
            let g:rustc_cmd = 'ptime rustc -o ..\target\test\$(VIM_FILENOEXT).exe $(VIM_FILEPATH) & ptime ..\target\test\$(VIM_FILENOEXT).exe'
        endif
    endif
    nnoremap ! :AsyncRun
    nnoremap <Tab>q :AsyncStop<CR>
    nnoremap <M-X>  :AsyncStop!<CR>
    if exists(':tnoremap')
        tnoremap <M-X> <C-\><C-n>:AsyncStop!<Cr>
    endif
else
    let s:run_command = "!"
    nnoremap ! :!
    xnoremap ! :<C-u>!<C-R>=GetVisualSelection()<Cr>
endif
" run now
function! s:run_now(...)
    w!
    if a:0 >= 2
        let pos = a:1
        let type = a:2
    elseif a:0 == 1
        let pos = a:1
        let type = 'term'
    else
        let pos = ''
        let type = 'qf'
    endif
    if !has('nvim') && v:version < 801
        let type = 'qf'
        if pos != ''
            let pos = 'right'
        endif
    endif
    " set params
    if s:run_command == "!"
        let params = " "
        let type = "qf"
    elseif pos == 'external'
        let params = ' -cwd=$(VIM_FILEDIR) -mode=term -focus=1 -pos=external'
    elseif type == 'term'
        if pos == 'tab'
            let params = ' -cwd=$(VIM_FILEDIR) -mode=term -focus=1 -pos=tab -reuse'
        elseif pos == 'floaterm_float'
            let params = ' -cwd=$(VIM_FILEDIR) -mode=term -focus=0 -pos=floaterm_float -width=0.382 -height=0.9'
        elseif pos == 'floaterm_right'
            let params = ' -cwd=$(VIM_FILEDIR) -mode=term -focus=0 -pos=floaterm_right -width=0.382'
        else
            let params = ' -cwd=$(VIM_FILEDIR) -mode=term -focus=0 -pos=bottom -rows=' . g:asyncrun_open
        endif
    else
        if has('patch-7.4.1829') || has('nvim')
            let params = ' -cwd=$(VIM_FILEDIR) -mode=async'
        else
            let params = ' -cwd=$(VIM_FILEDIR) -mode=bang'
        endif
    endif
    " create run_cmd
    let run_cmd = ''
    if WINDOWS()
        let time = ' ptime '
    else
        let time = ' time '
    endif
    if &filetype ==# 'dosbatch' && WINDOWS()
        let run_cmd = s:run_command . params. ' ptime %'
    elseif &filetype ==# 'sh' && executable('bash')
        let run_cmd = s:run_command . params . ' time bash %'
    elseif &filetype ==# 'python' && executable('python')
        let run_cmd = s:run_command . params . time . 'python %'
    elseif &filetype ==# 'r' && executable('Rscript')
        let run_cmd = s:run_command . params . time . 'Rscript %'
    elseif &filetype ==# 'perl' && executable('perl')
        let run_cmd = s:run_command . params . time . 'perl %'
    elseif &filetype ==# 'javascript' && executable('node')
        let run_cmd = s:run_command . params . time . 'node %'
    elseif &filetype ==# 'go' && executable('go')
        let run_cmd = s:run_command . params . time . 'go run %'
    elseif &filetype ==# 'vue' && executable('npm')
        let run_cmd = s:run_command . params . time . 'npm run %'
    elseif &filetype ==# 'c' && get(g:, 'gcc_cmd', '') != ''
        if WINDOWS()
            silent! call mkdir("../target/test", "p")
        endif
        let run_cmd = s:run_command . params . ' '. g:gcc_cmd
    elseif &filetype ==# 'cpp' && get(g:, 'gpp_cmd', '') != ''
        if WINDOWS()
            silent! call mkdir("../target/test", "p")
        endif
        let run_cmd = s:run_command . params . ' '. g:gpp_cmd
    elseif &filetype ==# 'rust' && get(g:, 'rustc_cmd', '') != ''
        if WINDOWS()
            silent! call mkdir("../target/test", "p")
        endif
        let run_cmd = s:run_command . params . ' '. g:rustc_cmd
    endif
    if empty(run_cmd)
        call preview#errmsg(&ft . ' could not be runned.')
    else
        exec run_cmd
        if type == 'qf'
            if pos == 'right'
                wincmd H
                execute "vertical resize " . float2nr(&columns * 0.6)
            else
                wincmd p
                execute 'copen ' . g:asyncrun_open
            endif
        endif
    endif
endfunction
command! RunQfBottom call s:run_now('', 'qf')
nnoremap <silent><M-B> :RunQfBottom<CR>
if has('nvim') || v:version >= 801
    let g:asyncrun_runner = get(g:, 'asyncrun_runner', {})
    command! RunTermTab call s:run_now('tab', 'term')
    nnoremap <silent><M-T> :RunTermTab<CR>
    command! RunTermBottom call s:run_now('', 'term')
    nnoremap <silent><M-R> :RunTermBottom<CR>
    if WINDOWS()
        command! RunExternal  call s:run_now('external')
        nnoremap <silent><M-F> :RunExternal<CR>
    else
        function! s:floaterm_close() abort
            if &ft == 'floaterm' | return | endif
            for b in tabpagebuflist()
                if getbufvar(b, '&ft') == 'floaterm' &&
                            \ getbufvar(b, 'floaterm_jobexists') == v:false
                    execute b 'bwipeout!'
                    break
                endif
            endfor
            autocmd! close-floaterm-runner
        endfunc
        if has('nvim')
            function! s:floaterm_float(opts)
                let cmd = 'FloatermNew '
                let cmd .= ' --wintype=float --position=topright --name=floaterm_float'
                if has_key(a:opts, 'width')
                    let cmd .= ' --width=' . fnameescape(a:opts.width)
                endif
                if has_key(a:opts, 'height')
                    let cmd .= ' --height=' . fnameescape(a:opts.height)
                endif
                if has_key(a:opts, 'title')
                    let cmd .= ' --title=' . fnameescape(a:opts.title)
                endif
                let cmd .= ' --autoclose=' . get(a:opts, 'autoclose', 0)
                let cmd .= ' --silent=' . get(a:opts, 'silent', 0)
                let cwd = (a:opts.cwd == '')? getcwd() : (a:opts.cwd)
                let cmd .= ' --cwd=' . fnameescape(cwd)
                let cmd .= ' ' . fnameescape(asyncrun#script_write(a:opts.cmd, 0))
                exec cmd
                if get(a:opts, 'focus', 1) == 0
                    stopinsert | noa wincmd p
                    " augroup close-floaterm-runner
                    "     autocmd!
                    "     autocmd CursorMoved,InsertEnter * ++nested
                    "                 \ call timer_start(100, { -> s:floaterm_close() })
                    " augroup END
                endif
            endfunction
            let g:asyncrun_runner.floaterm_float = function('s:floaterm_float')
            command! RunFloatermFloat call s:run_now('floaterm_float', 'term')
            nnoremap <silent><M-F> :RunFloatermFloat<CR>
        else
            function! s:floaterm_right(opts)
                let cmd = 'FloatermNew '
                let cmd .= ' --wintype=vsplit --position=right --name=floaterm_right'
                if has_key(a:opts, 'width')
                    let cmd .= ' --width=' . fnameescape(a:opts.width)
                endif
                if has_key(a:opts, 'title')
                    let cmd .= ' --title=' . fnameescape(a:opts.title)
                endif
                let cmd .= ' --autoclose=' . get(a:opts, 'autoclose', 0)
                let cmd .= ' --silent=' . get(a:opts, 'silent', 0)
                let cwd = (a:opts.cwd == '')? getcwd() : (a:opts.cwd)
                let cmd .= ' --cwd=' . fnameescape(cwd)
                let cmd .= ' ' . fnameescape(asyncrun#script_write(a:opts.cmd, 0))
                exec cmd
                if get(a:opts, 'focus', 1) == 0
                    stopinsert | noa wincmd p
                endif
            endfunc
            let g:asyncrun_runner.floaterm_right = function('s:floaterm_right')
            command! RunFloatermRight call s:run_now('floaterm_right', 'term')
            nnoremap <silent><M-F> :RunFloatermRight<CR>
        endif
    endif
else
    command! RunQfRight call s:run_now('right', 'qf')
    nnoremap <silent><M-F> :RunQfRight<CR>
    nnoremap <M-R> :call preview#errmsg("Please update to vim8.1+/nvim0.4+ to run script in terminal.")<Cr>
    nnoremap <M-T> :call preview#errmsg("Please update to vim8.1+/nvim0.4+ to run script in terminal.")<Cr>
endif
" ----------------
" asynctasks
" ----------------
if has('nvim') || v:version >= 801
    let g:asyncrun_rootmarks     = ['.git', '.hg', '.svn', '.root', '.project']
    let g:asynctasks_config_name = [".tasks", ".git/tasks.ini", ".hg/tasks.ini", ".svn/tasks.ini", ".root/tasks.ini", ".project/tasks.ini"]
    let g:asynctasks_rtp_config  = "tasks.ini"
    let g:asynctasks_term_reuse  = 1
    let g:asynctasks_term_focus  = 0
    let g:asynctasks_term_listed = 0
    let g:asynctasks_sort        = 1
    " template
    let g:asynctasks_template = '~/.leovim/tasks/tasks_template.ini'
    let g:asynctasks_extra_config = [
                \ '~/.leovim/tasks/tasks.ini',
                \ '~/.leovim.d/tasks/tasks.ini',
                \ ]
    " packadd
    PlugAddOpt 'asynctasks.vim'
    " open template
    nnoremap <leader>r<Cr>  :tabe $LEOVIM_PATH/tasks/tasks_example.ini<Cr>
    nnoremap <leader>r<Tab> :silent! mkdir(Expand("~/.leovim.d/tasks"), 'p')<Cr>:tabe $HOME/.leovim.d/tasks/tasks.ini<Cr>
    " asynctask shortcuts
    nnoremap <leader>ra :AsyncTask
    nnoremap <leader>rm :AsyncTaskMacro<Cr>
    nnoremap <leader>re :AsyncTaskEdit<Space>
    nnoremap <leader>rl :AsyncTaskList<Space>
    " run shortcuts
    nnoremap <leader>rr :AsyncTask project-run<Cr>
    nnoremap <leader>ri :AsyncTask project-init<Cr>
    nnoremap <leader>rb :AsyncTask project-build<Cr>
    nnoremap <leader>rd :AsyncTask project-debug<Cr>
    nnoremap <leader>rc :AsyncTask project-compile<Cr>
    nnoremap <leader>rt :AsyncTask project-test<Cr>
    function! AsyncTaskProfileLoop() abort
        if get(g:, 'asynctasks_profile', '') == ''
            let g:asynctasks_profile = 'debug'
        elseif g:asynctasks_profile == 'debug'
            let g:asynctasks_profile = 'build'
        elseif g:asynctasks_profile == 'build'
            let g:asynctasks_profile = 'release'
        elseif g:asynctasks_profile == 'release'
            let g:asynctasks_profile = 'debug'
        endif
        echom "asynctasks_profile is " . g:asynctasks_profile
    endfunction
    command! AsyncTaskProfileLoop call AsyncTaskProfileLoop()
    nnoremap <leader>rp :<C-u>AsyncTaskProfileLoop<CR>
    nnoremap <leader>rf :<C-u>AsyncTaskProfile<CR>
    if InstalledFzf() && (get(g:, 'prefer_fzf', UNIX()) || !Installed('leaderf'))
        function! s:fzf_sink(what)
            let p1 = stridx(a:what, '<')
            if p1 >= 0
                let name = strpart(a:what, 0, p1)
                let name = substitute(name, '^\s*\(.\{-}\)\s*$', '\1', '')
                if name != ''
                    exec "AsyncTask ". fnameescape(name)
                endif
            endif
        endfunction
        function! s:fzf_tasks()
            let rows = asynctasks#source(&columns * 48 / 100)
            let source = []
            for row in rows
                let name = row[0]
                let source = [name . '  ' . row[1] . '  : ' . row[2]] + source
            endfor
            let opts = {'source': source, 'sink': function('s:fzf_sink'), 'options': '+m --nth 1 --inline-info --tac' }
            if exists('g:fzf_layout')
                for key in keys(g:fzf_layout)
                    let opts[key] = deepcopy(g:fzf_layout[key])
                endfor
            endif
            call fzf#run(opts)
        endfunction
        command! -nargs=0 FzfAsyncTasks call s:fzf_tasks()
        nnoremap <silent><M-r> :FzfAsyncTasks<Cr>
    elseif Installed('leaderf')
        function! s:lf_task_source(...)
            let rows = asynctasks#source(&columns * 48 / 100)
            let source = []
            for row in rows
                let name = row[0]
                let source += [name . '  ' . row[1] . '  : ' . row[2]]
            endfor
            return source
        endfunc
        function! s:lf_task_accept(line, arg)
            let pos = stridx(a:line, '<')
            if pos < 0
                return
            endif
            let name = strpart(a:line, 0, pos)
            let name = substitute(name, '^\s*\(.\{-}\)\s*$', '\1', '')
            if name != ''
                exec "AsyncTask " . name
            endif
        endfunc
        function! s:lf_task_digest(line, mode)
            let pos = stridx(a:line, '<')
            if pos < 0
                return [a:line, 0]
            endif
            let name = strpart(a:line, 0, pos)
            return [name, 0]
        endfunc
        function! s:lf_win_init(...)
            setlocal nonumber
            setlocal nowrap
        endfunc
        let g:Lf_Extensions = get(g:, 'Lf_Extensions', {})
        let g:Lf_Extensions.tasks = {
                    \ 'source': string(function('s:lf_task_source'))[10:-3],
                    \ 'accept': string(function('s:lf_task_accept'))[10:-3],
                    \ 'get_digest': string(function('s:lf_task_digest'))[10:-3],
                    \ 'highlights_def': {
                        \     'Lf_hl_funcScope': '^\S\+',
                        \     'Lf_hl_funcDirname': '^\S\+\s*\zs<.*>\ze\s*:',
                        \ },
                        \ }
        nnoremap <silent><M-r> :Leaderf tasks --popup-width=0.9 --popup-height=0.6 --no-auto-preview<Cr>
    endif
endif
" ---------------------------------
" git global set
" ---------------------------------
if executable('git') && get(g:, 'header_field_author', '') != '' && get(g:, 'header_field_author_email', '') != ''
    command! GitConfig execute(printf(
                \ '%s git config --global user.name "%s" && git config --global user.email "%s"',
                \ Installed('asyncrun.vim') ? 'AsyncRun!' : '!',
                \ g:header_field_author,
                \ g:header_field_author_email))
    nnoremap <M-g>g :GitConfig<Cr>
endif
